﻿Imports System.IO
Imports System.Text.Json

Public Class TaskInfo
    ' 设置方案A的颜色
    Dim AccentColor As Color = Color.FromArgb(37, 99, 235)  ' 蓝色：#2563EB
    Dim AccentColorHover As Color = Color.FromArgb(29, 78, 216)  ' 蓝色悬停色：#1D4ED8
    Dim TextPrimary As Color = Color.FromArgb(31, 41, 55)  ' 深灰：#1F2937
    Dim TextSecondary As Color = Color.FromArgb(107, 114, 128)  ' 次灰：#6B7280
    Dim DividerColor As Color = Color.FromArgb(229, 231, 235)  ' 灰色：#E5E7EB
    Dim OkDotColor As Color = Color.FromArgb(16, 185, 129)  ' 绿色：#10B981
    Dim DueSoonDotColor As Color = Color.FromArgb(245, 158, 11)  ' 橙色：#F59E0B
    Dim OverdueDotColor As Color = Color.FromArgb(239, 68, 68)  ' 红色：#EF4444
    Dim BackgroundOverlayColor As Color = Color.FromArgb(204, 255, 255, 255) ' 背景叠加白色透明

    '--- 添加窗口拖动和调整大小的变量 ---
    Private isMouseDown As Boolean = False
    Private mouseOffset As Point
    Private resizeAreaSize As Integer = 10  ' 窗口可调整大小的区域大小

    ' 获取应用程序可执行文件所在目录
    Public appDirectory As String = Application.StartupPath
    ' 创建待办任务数据存储路径
    Public todoDataPath As String = Path.Combine(appDirectory, "TodoReminder")
    ' 创建配置文件存储路径
    Public configFilePath As String = Path.Combine(appDirectory, "AppConfig")
    Public logFilePath As String = Path.Combine(appDirectory, "Log")
    Private ReadOnly dataDir As String = todoDataPath
    Private ReadOnly dataFile As String = Path.Combine(dataDir, "tasks.json")
    Private ReadOnly ColorFile As String = Path.Combine(configFilePath, "backgroundColor.txt")
    Private ReadOnly WidthConfigFile As String = Path.Combine(configFilePath, "width.txt")

    ' ======== 备忘区 UI 成员 ========
    Private PanelMemo As Panel
    Private lblMemoHeader As Label
    Private txtMemoSearch As TextBox
    Private btnMemoAdd As Button
    ' ======== 任务区与备忘区的分隔与控件 ========
    Private SplitTasksMemo As SplitContainer
    Private MemoHeaderPanel As Panel
    Private lblMemoTitle As Label

    ' 备忘右键菜单
    Private CMS_Memo As ContextMenuStrip
    Private WithEvents TSMI_MemoNew As ToolStripMenuItem
    Private WithEvents TSMI_MemoEdit As ToolStripMenuItem
    Private WithEvents TSMI_MemoDelete As ToolStripMenuItem
    Private WithEvents TSMI_MemoDone As ToolStripMenuItem

    ' 统一的外观控制
    Private ReadOnly MemoHeaderHeight As Integer = 36
    Private ReadOnly MemoRowGap As Integer = 6
    Private ReadOnly MemoSidePadding As Integer = 16

    ' 提前提醒的分钟数
    Private Const ReminderLeadMinutes As Integer = 30

    Public Sub New()
        InitializeComponent()

        Me.DoubleBuffered = True  ' 启用双缓冲
    End Sub

    Private Sub FormMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Logger.Log("程序启动")
        Me.Hide()

        ' 读取透明度设置
        LoadOpacitySetting()
        ' 初始化NotifyIcon
        InitializeNotifyIcon()

        ' 目录与颜色
        If Not Directory.Exists(todoDataPath) Then Directory.CreateDirectory(todoDataPath)
        If Not Directory.Exists(configFilePath) Then Directory.CreateDirectory(configFilePath)
        If Not Directory.Exists(logFilePath) Then Directory.CreateDirectory(logFilePath)
        If File.Exists(ColorFile) Then
            Dim savedColor As String = File.ReadAllText(ColorFile)
            Me.BackColor = Color.FromArgb(Integer.Parse(savedColor))
        End If

        Me.FormBorderStyle = FormBorderStyle.None

        '读取任务栏宽度数据
        If File.Exists(WidthConfigFile) Then
            LoadWidthSetting()
        End If

        If Me.Width < 200 Or Me.Width > 600 Then  ' 宽度限制在 200-600 之间
            Me.Width = 320
        End If

        DockRight()

        ' 原任务区容器保持
        PanelMain.BackColor = BackgroundOverlayColor
        PanelMain.AutoScroll = True

        ' 1) 先创建可拖动分隔条与备忘区（唯一入口）
        SetupTasksMemoSplitter()

        ' 2) 绑定事件（只绑定一次）
        AddHandler TaskStore.TasksChanged, Sub() SafeUI(Sub() RenderTasks())
        AddHandler MemoStore.MemosChanged, Sub() SafeUI(Sub() RenderMemos())

        ' 3) 载入任务（MemoStore 在 Shared 构造中已加载）
        TaskStore.LoadTasksFromDisk()

        ' 窗口嵌入桌面、其他初始化
        DesktopEmbed.EmbedToDesktop(Me)
        Me.ShowInTaskbar = False

        TimerCheck.Enabled = True

        ' 示例数据（可选）
        If TaskStore.Tasks.Count = 0 Then
            TaskStore.Add(New TaskItem With {.TaskDate = Date.Today, .Description = "上午开会", .DueAt = Date.Today.AddHours(9)})
            TaskStore.Add(New TaskItem With {.TaskDate = Date.Today, .Description = "下午写报告", .DueAt = Date.Today.AddHours(14)})
            TaskStore.Add(New TaskItem With {.TaskDate = Date.Today.AddDays(1), .Description = "交付项目文档", .DueAt = Date.Today.AddDays(1).AddHours(10)})
        End If

        SetControlRound(Me, 10)

        ' 4) 首次渲染（注意：RenderMemos 独立调用，不依赖 RenderTasks）
        RenderTasks()
        RenderMemos()

        ' 窗口显示控制
        If SettingsHelper.ShouldShowWindowA() Then Me.Show() Else Me.Hide()
        If SettingsHelper.ShouldShowWindowB() Then FormCalendar.Show() Else FormCalendar.Hide()

    End Sub

    Private Sub SetupTasksMemoSplitter()
        If SplitTasksMemo IsNot Nothing Then Return

        ' 1) 创建分隔器并放到窗体
        Dim split = New SplitContainer() With {
        .Orientation = Orientation.Horizontal,
        .Dock = DockStyle.Fill,
        .SplitterWidth = 6,
        .Panel1MinSize = 120,
        .Panel2MinSize = 120,
        .FixedPanel = FixedPanel.None
    }
        SplitTasksMemo = split

        ' 2) 把原 PanelMain 移入 Panel1（保持你原有的任务渲染逻辑不改）
        If Me.Controls.Contains(PanelMain) Then Me.Controls.Remove(PanelMain)
        split.Panel1.Controls.Add(PanelMain)
        PanelMain.Dock = DockStyle.Fill
        PanelMain.AutoScroll = True

        ' 3) 备忘右键菜单（用 AddHandler 绑定，不用 Handles）
        CMS_Memo = New ContextMenuStrip()
        TSMI_MemoNew = New ToolStripMenuItem("新增备忘") With {.Name = "TSMI_MemoNew"}
        TSMI_MemoEdit = New ToolStripMenuItem("编辑") With {.Name = "TSMI_MemoEdit"}
        TSMI_MemoDelete = New ToolStripMenuItem("删除") With {.Name = "TSMI_MemoDelete"}
        TSMI_MemoDone = New ToolStripMenuItem("标记完成（自动移除）") With {.Name = "TSMI_MemoDone"}

        CMS_Memo.Items.AddRange(New ToolStripItem() {
        TSMI_MemoNew, TSMI_MemoEdit, TSMI_MemoDelete, New ToolStripSeparator(), TSMI_MemoDone
    })

        AddHandler CMS_Memo.Opening, AddressOf CMS_Memo_Opening
        AddHandler TSMI_MemoNew.Click, AddressOf TSMI_MemoNew_Click
        AddHandler TSMI_MemoEdit.Click, AddressOf TSMI_MemoEdit_Click
        AddHandler TSMI_MemoDelete.Click, AddressOf TSMI_MemoDelete_Click
        AddHandler TSMI_MemoDone.Click, AddressOf TSMI_MemoDone_Click

        ' 4) 创建备忘标题+搜索
        MemoHeaderPanel = New Panel() With {.Dock = DockStyle.Top, .Height = 36, .BackColor = DividerColor}
        lblMemoTitle = New Label() With {
        .Text = "备忘",
        .AutoSize = False,
        .Dock = DockStyle.Left,
        .Width = 100,
        .TextAlign = ContentAlignment.MiddleLeft,
        .Font = New Font("Microsoft YaHei", 10, FontStyle.Bold),
        .ForeColor = TextPrimary
    }
        txtMemoSearch = New TextBox() With {.Dock = DockStyle.Right, .Width = 160, .BorderStyle = BorderStyle.FixedSingle}
        AddHandler txtMemoSearch.TextChanged, Sub() RenderMemos()

        MemoHeaderPanel.Controls.Add(txtMemoSearch)
        MemoHeaderPanel.Controls.Add(lblMemoTitle)

        ' 5) 创建备忘列表面板
        PanelMemo = New Panel With {
            .Dock = DockStyle.Fill, .AutoScroll = True, .BackColor = PanelMain.BackColor,
            .ContextMenuStrip = CMS_Memo
        }

        ' 6) 放到分隔器 Panel2
        split.Panel2.Controls.Add(PanelMemo)
        split.Panel2.Controls.Add(MemoHeaderPanel)

        ' 7) 分隔器加入窗体（位于最上层）
        Me.Controls.Add(split)
        split.BringToFront()

        ' 8) 初始分隔位置（上 2/3 为待办，下 1/3 为备忘，可自行调整）
        SplitTasksMemo.SplitterDistance = CInt(Me.ClientSize.Height * 0.65)
    End Sub

    Private Sub InitializeNotifyIcon()
        If NotifyIcon1 Is Nothing Then
            NotifyIcon1 = New NotifyIcon()
        End If

        ' 设置图标 - 使用应用程序图标或创建简单图标
        Try

            If NotifyIcon1.Icon Is Nothing Then
                ' 创建一个简单的16x16图标
                Dim bmp As New Bitmap(16, 16)
                Using g As Graphics = Graphics.FromImage(bmp)
                    g.Clear(Color.Blue)
                    g.FillEllipse(Brushes.White, 2, 2, 12, 12)
                End Using
                NotifyIcon1.Icon = Icon.FromHandle(bmp.GetHicon())
            End If
        Catch ex As Exception
            ' 如果无法获取图标，创建一个默认图标
            Dim bmp As New Bitmap(16, 16)
            Using g As Graphics = Graphics.FromImage(bmp)
                g.Clear(Color.Blue)
                g.FillEllipse(Brushes.White, 2, 2, 12, 12)
            End Using
            NotifyIcon1.Icon = Icon.FromHandle(bmp.GetHicon())
        End Try

        ' 设置基本属性
        NotifyIcon1.Text = "待办提醒"
        NotifyIcon1.Visible = True

        ' 设置右键菜单
        NotifyIcon1.ContextMenuStrip = IconMenu

        ' 添加点击事件处理
        AddHandler NotifyIcon1.Click, AddressOf NotifyIcon1_Click

    End Sub

    ' 单击事件：显示TaskInfo窗口
    Private Sub NotifyIcon1_Click(sender As Object, e As EventArgs)
        ' 只处理左键单击
        If TypeOf e Is MouseEventArgs Then
            Dim mouseArgs = DirectCast(e, MouseEventArgs)
            If mouseArgs.Button = MouseButtons.Left Then
                Me.Show()
                Me.WindowState = FormWindowState.Normal
                Me.BringToFront()
                Me.TopMost = True
                ' 短暂置顶后取消，避免一直在最前面
                Task.Delay(500).ContinueWith(Sub()
                                                 If Me.IsHandleCreated Then
                                                     Me.Invoke(Sub() Me.TopMost = False)
                                                 End If
                                             End Sub)
            End If
        End If
    End Sub

    Private Sub SafeUI(action As Action)
        If Me.IsHandleCreated AndAlso Me.InvokeRequired Then
            Me.BeginInvoke(action)
        Else
            action()
        End If
    End Sub

    Private Sub FormMain_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
        ' 保存窗口 A 的显示状态
        SettingsHelper.SetWindowAState(Me.Visible)
        ' 保存窗口 B 的显示状态
        SettingsHelper.SetWindowBState(FormCalendar.Visible)


        SaveTasksToDisk()

        ' 清理NotifyIcon资源
        If NotifyIcon1 IsNot Nothing Then
            NotifyIcon1.Visible = False
            NotifyIcon1.Dispose()
        End If

        Logger.Log("程序退出")
    End Sub

    Private Sub DockRight()
        Dim r = Screen.PrimaryScreen.WorkingArea
        Me.Location = New Point(r.Right - Me.Width, r.Top)
        Me.Height = r.Height
    End Sub

    Public Sub RenderTasks()
        Me.SuspendLayout()
        PanelMain.SuspendLayout()
        PanelMain.Controls.Clear()

        Dim today = Date.Today
        Dim visibleTasks = TaskStore.Tasks.
        Where(Function(t) t.TaskDate.Date >= today).
        ToList()

        If visibleTasks.Count = 0 Then
            Dim lblEmpty As New Label() With {
            .AutoSize = True,
            .Left = 16,
            .Top = 16,
            .Text = "暂无今天及以后的任务",
            .Font = New Font("Microsoft YaHei", 9, FontStyle.Italic),
            .ForeColor = TextSecondary
        }
            PanelMain.Controls.Add(lblEmpty)
            PanelMain.ResumeLayout()
            Me.ResumeLayout()
            Return
        End If

        Dim y As Integer = 10
        Dim dates = visibleTasks.
        Select(Function(t) t.TaskDate.Date).
        Distinct().
        OrderBy(Function(d) d)

        For Each d In dates
            Dim lblDate As New Label With {
                .Text = d.ToString("yyyy-MM-dd"),
                .Font = New Font("Microsoft YaHei", 10, FontStyle.Bold),
                .AutoSize = True,
                .Location = New Point(10, y),
                .Tag = d,
                .BackColor = AccentColor,
                .ForeColor = Color.White
            }
            AddHandler lblDate.DoubleClick, AddressOf lblDate_DoubleClick
            PanelMain.Controls.Add(lblDate)
            y += lblDate.Height + 6

            Dim dayTasks = visibleTasks.
            Where(Function(t) t.TaskDate.Date = d).
            OrderBy(Function(t) t.DueAt).
            ToList()

            For Each t In dayTasks
                Dim pnl As New Panel() With {
                .Width = PanelMain.ClientSize.Width - 24,
                .Height = 28,
                .Left = 16,
                .Top = y,
                .BackColor = PanelMain.BackColor,
                .Anchor = AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top
            }

                Dim lblTime As New Label() With {
                .AutoSize = True,
                .Left = 0,
                .Top = 6,
                .Text = t.DueAt.ToString("HH:mm"),
                .Tag = t.Id
            }

                Dim lblTask As New Label() With {
                .AutoSize = False,
                .Left = 60,
                .Top = 6,
                .Width = Math.Max(60, pnl.Width - 70),
                .Text = t.Description,
                .Tag = t.Id,
                .ContextMenuStrip = CMS_Task,
                .TextAlign = ContentAlignment.TopLeft,
                .UseCompatibleTextRendering = True,
                .BackColor = Color.Transparent
            }

                ApplyTaskLabelStyle(lblTime, t)
                ApplyTaskLabelStyle(lblTask, t)

                Dim proposed As New Size(lblTask.Width, Integer.MaxValue)
                Dim flags As TextFormatFlags = TextFormatFlags.WordBreak Or TextFormatFlags.TextBoxControl Or TextFormatFlags.NoPadding
                Dim sz As Size = TextRenderer.MeasureText(lblTask.Text, lblTask.Font, proposed, flags)
                lblTask.Height = sz.Height

                lblTime.ContextMenuStrip = CMS_Task
                pnl.ContextMenuStrip = CMS_Task
                pnl.Tag = t.Id

                pnl.Height = Math.Max(lblTime.Bottom, lblTask.Bottom) + 6
                pnl.Controls.Add(lblTime)
                pnl.Controls.Add(lblTask)
                PanelMain.Controls.Add(pnl)

                y += pnl.Height + 4
            Next

            y += 8
        Next

        PanelMain.ResumeLayout()
        Me.ResumeLayout()
    End Sub

    Private Sub RenderMemos()
        If PanelMemo Is Nothing Then Return

        PanelMemo.SuspendLayout()
        PanelMemo.Controls.Clear()

        Dim kw As String = If(txtMemoSearch Is Nothing, "", txtMemoSearch.Text).Trim()
        Dim q = MemoStore.Memos.AsEnumerable()
        If kw <> "" Then
            q = q.Where(Function(m) Not String.IsNullOrEmpty(m.Text) AndAlso
                             m.Text.IndexOf(kw, StringComparison.OrdinalIgnoreCase) >= 0)
        End If

        q = q.OrderBy(Function(m) m.IsCompleted).ThenByDescending(Function(m) m.CreatedAt)

        Dim y As Integer = 6
        Dim innerPadding As Integer = 6
        Dim width = Math.Max(10, PanelMemo.ClientSize.Width - innerPadding * 2)

        For Each m In q
            Dim lbl As New Label With {
                .AutoSize = False,
            .Left = innerPadding,
            .Top = y,
            .Width = width,
            .Text = m.Text,
            .Tag = m,
            .ContextMenuStrip = CMS_Memo,
            .BackColor = Color.Transparent,
            .TextAlign = ContentAlignment.TopLeft,
                .UseCompatibleTextRendering = True,
                .ForeColor = If(m.IsCompleted, TextSecondary, TextPrimary),
                .Font = New Font("Microsoft YaHei", 9, If(m.IsCompleted, FontStyle.Strikeout, FontStyle.Regular))
            }
            'AddHandler lbl.DoubleClick, Sub() MemoEdit(m)
            Dim m0 = m
            AddHandler lbl.DoubleClick, Sub() MemoEdit(m0)

            Dim measured = TextRenderer.MeasureText(lbl.Text, lbl.Font, New Size(lbl.Width, Integer.MaxValue),
                                                TextFormatFlags.WordBreak Or TextFormatFlags.TextBoxControl Or TextFormatFlags.NoPadding)
            lbl.Height = Math.Max(22, measured.Height)

            PanelMemo.Controls.Add(lbl)
            y += lbl.Height + innerPadding
        Next

        PanelMemo.ResumeLayout()
    End Sub

    Private Function GetMemoFromAny(src As Control) As MemoItem
        Dim p = src
        While p IsNot Nothing
            If TypeOf p.Tag Is MemoItem Then Return DirectCast(p.Tag, MemoItem)
            p = p.Parent
        End While
        Return Nothing
    End Function

    Private Sub MemoNew()
        Dim txt = InputBox("请输入备忘内容：", "新增备忘").Trim()
        If txt = "" Then Return
        MemoStore.Add(New MemoItem With {.Text = txt})
    End Sub

    Private Sub MemoEdit(m As MemoItem)
        If m Is Nothing Then Return
        Dim txt = InputBox("编辑备忘：", "编辑备忘", m.Text).Trim()
        If txt = "" Then Return
        m.Text = txt
        MemoStore.Update(m)
    End Sub

    ' 右键菜单事件（全部用 AddHandler 绑定，无 Handles）
    Private Sub CMS_Memo_Opening(sender As Object, e As System.ComponentModel.CancelEventArgs)
        Dim src = DirectCast(sender, ContextMenuStrip).SourceControl
        Dim mm = GetMemoFromAny(src)
        TSMI_MemoEdit.Enabled = (mm IsNot Nothing)
        TSMI_MemoDelete.Enabled = (mm IsNot Nothing)
        TSMI_MemoDone.Enabled = (mm IsNot Nothing)
    End Sub

    Private Sub TSMI_MemoNew_Click(sender As Object, e As EventArgs)
        MemoNew()
    End Sub

    Private Sub TSMI_MemoEdit_Click(sender As Object, e As EventArgs)
        Dim mm = GetMemoFromAny(CMS_Memo.SourceControl)
        If mm IsNot Nothing Then MemoEdit(mm)
    End Sub

    Private Sub TSMI_MemoDelete_Click(sender As Object, e As EventArgs)
        Dim mm = GetMemoFromAny(CMS_Memo.SourceControl)
        If mm IsNot Nothing Then MemoStore.Remove(mm.Id)
    End Sub

    Private Sub TSMI_MemoDone_Click(sender As Object, e As EventArgs)
        Dim mm = GetMemoFromAny(CMS_Memo.SourceControl)
        If mm IsNot Nothing Then
            mm.IsCompleted = True
            MemoStore.Update(mm)
            MemoStore.Remove(mm.Id)  ' 完成后即从列表移除
        End If
    End Sub
    '==============================================================

    '双击日期：添加当日任务
    Private Sub lblDate_DoubleClick(sender As Object, e As EventArgs)
        Dim d As Date = CType(CType(sender, Label).Tag, Date)
        Dim frm As New FormEditTask() With {.TaskDate = d, .DueAt = d.AddHours(9)}
        If frm.ShowDialog() = DialogResult.OK Then
            TaskStore.Add(New TaskItem With {
            .TaskDate = frm.TaskDate.Date,
            .Description = frm.TaskDescription,
            .DueAt = frm.DueAt,
            .IsCompleted = False,
            .HasNotified = False
        })
            ' 不再手动 RenderTasks()，事件会驱动
        End If

    End Sub

    ' 打开右键菜单时，动态显示“标记完成/取消完成”
    Private Sub CMS_Task_Opening(sender As Object, e As System.ComponentModel.CancelEventArgs) Handles CMS_Task.Opening
        Dim cms = DirectCast(sender, ContextMenuStrip)
        Dim src = cms.SourceControl

        ' 根据触发源获取任务
        Dim task = TryCast(GetTaskFromSource(src), TaskItem)

        ' 动态设置菜单项文字
        If task Is Nothing Then Exit Sub

        ' 保险：按名称查找，避免 Name 未设置时抛错
        Dim doneItem = cms.Items.Cast(Of ToolStripItem)().
        FirstOrDefault(Function(i) i.Name = "TSMI_Done")

        If doneItem IsNot Nothing Then
            doneItem.Text = If(task.IsCompleted, "取消完成", "标记完成")
        End If
    End Sub

    ' 右键菜单点击
    Private Sub CMS_Task_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles CMS_Task.ItemClicked
        Dim cms = DirectCast(sender, ContextMenuStrip)
        Dim src = cms.SourceControl

        ' 先从 SourceControl 获取任务
        Dim task = GetTaskFromAny(src)

        Select Case e.ClickedItem.Name
            Case "TSMI_New"
                ' 新建任务：根据触发源推断日期/时间默认
                Dim d As Date, due As DateTime
                InferDateTimeFromSource(src, d, due)

                ' 创建新任务
                'Dim frm As New FormEditTask() With {.TaskDate = Date.Today, .DueAt = Date.Today.AddHours(9)} ' 默认日期和时间
                Dim frm As New FormEditTask() With {
                .TaskDate = d,
                .DueAt = due
                }

                If frm.ShowDialog() = DialogResult.OK Then
                    ' 获取用户输入的新任务信息
                    Dim newTask As New TaskItem With {
                    .TaskDate = frm.TaskDate.Date,
                    .Description = frm.TaskDescription,
                    .DueAt = frm.DueAt,
                    .IsCompleted = False,
                    .HasNotified = False
                }
                    TaskStore.Add(newTask)  ' 新建任务并保存
                End If

            Case "TSMI_EditTime"
                ' 编辑已有任务；若右键在日期标题上误点编辑，则引导改为新建
                If task Is Nothing Then
                    ' 可选：直接当“新建”用，或给出提示
                    Dim d As Date, due As DateTime
                    InferDateTimeFromSource(src, d, due)
                    Dim frm As New FormEditTask() With {.TaskDate = d, .DueAt = due}
                    If frm.ShowDialog() = DialogResult.OK Then
                        Dim newTask As New TaskItem With {
                        .TaskDate = frm.TaskDate.Date,
                        .Description = frm.TaskDescription,
                        .DueAt = frm.DueAt,
                        .IsCompleted = False,
                        .HasNotified = False
                    }
                        TaskStore.Add(newTask)
                    End If
                Else
                    Dim frm As New FormEditTask() With {
                    .TaskDate = task.TaskDate,
                    .TaskDescription = task.Description,
                    .DueAt = task.DueAt
                }
                    If frm.ShowDialog() = DialogResult.OK Then
                        task.TaskDate = frm.TaskDate.Date
                        task.Description = frm.TaskDescription
                        task.DueAt = frm.DueAt
                        task.HasNotified = False
                        TaskStore.Update(task)
                    End If
                End If

            Case "TSMI_Done"
                If task Is Nothing Then Exit Select
                task.IsCompleted = Not task.IsCompleted
                If Not task.IsCompleted Then task.HasNotified = False
                TaskStore.Update(task)

            Case "TSMI_Delete"
                If task Is Nothing Then Exit Select
                TaskStore.Remove(task)

            Case "TSMI_colorDialog"
                ' 修复变量名大小写误用（使用实例而不是类型名）
                Dim dlg As New ColorDialog()
                If dlg.ShowDialog() = DialogResult.OK Then
                    Me.BackColor = dlg.Color
                    Dim colorToSave As String = dlg.Color.ToArgb().ToString()
                    File.WriteAllText(Path.Combine(configFilePath, "backgroundColor.txt"), colorToSave)
                End If
                ' 新增调整透明度菜单项
            Case "TSMI_Opacity"
                ShowOpacityDialog()

            Case "TSMI_ModiWidth"
                Dim input As String = InputBox("请输入窗口宽度 (200-600):", "调整宽度", Me.Width.ToString())
                Dim newWidth As Integer

                If Integer.TryParse(input, newWidth) Then
                    If newWidth >= 200 AndAlso newWidth <= 600 Then
                        Me.Width = newWidth
                        ' 保存设置到配置文件
                        SaveWidthSetting(newWidth)
                    Else
                        MessageBox.Show("宽度必须在200到600之间", "无效的宽度", MessageBoxButtons.OK, MessageBoxIcon.Warning)
                    End If
                End If
                DockRight()
                DesktopEmbed.EmbedToDesktop(Me)
                SetControlRound(Me, 10)
                RenderTasks()
                RenderMemos()
        End Select
    End Sub

    Private Sub SaveWidthSetting(width As Integer)
        Try
            File.WriteAllText(WidthConfigFile, width.ToString())
        Catch ex As Exception
            MessageBox.Show("保存宽度设置失败：" & ex.Message)
        End Try
    End Sub

    Private Sub LoadWidthSetting()
        Try
            If File.Exists(WidthConfigFile) Then
                Dim widthStr As String = File.ReadAllText(WidthConfigFile)
                Dim width As Integer
                If Integer.TryParse(widthStr, width) Then
                    Me.Width = width
                End If
            End If
        Catch ex As Exception
            ' 忽略错误，使用默认宽度
        End Try
    End Sub

    ' 调节透明度的窗口
    Private Sub ShowOpacityDialog()
        ' 创建一个新窗体用于透明度调节
        Dim opacityForm As New Form() With {
        .Text = "调整窗口透明度",
        .Size = New Size(300, 150),
        .FormBorderStyle = FormBorderStyle.FixedDialog,
        .StartPosition = FormStartPosition.CenterParent,
        .MaximizeBox = False,
        .MinimizeBox = False
    }

        ' 创建一个 TrackBar 控件，用于调整透明度
        Dim opacityTrackBar As New TrackBar() With {
        .Minimum = 0,  ' 透明度最小值（完全透明）
        .Maximum = 100, ' 透明度最大值（完全不透明）
        .Value = CInt(Me.Opacity * 100), ' 设置为当前窗口透明度（0-100之间）
        .TickStyle = TickStyle.None,  ' 去掉刻度线
        .Size = New Size(250, 45),
        .Location = New Point(20, 20)
    }

        ' 创建标签来显示当前透明度百分比
        Dim lblOpacity As New Label() With {
        .Text = $"透明度：{opacityTrackBar.Value}%",
        .Location = New Point(20, 90),
        .AutoSize = True
    }

        ' 添加 TrackBar 控件和标签到窗体
        opacityForm.Controls.Add(opacityTrackBar)
        opacityForm.Controls.Add(lblOpacity)

        ' 当 TrackBar 的值发生变化时，更新透明度和标签
        AddHandler opacityTrackBar.ValueChanged, Sub(sender As Object, e As EventArgs)
                                                     Me.Opacity = opacityTrackBar.Value / 100.0
                                                     lblOpacity.Text = $"透明度：{opacityTrackBar.Value}%"
                                                 End Sub

        ' 显示透明度调节窗体
        opacityForm.ShowDialog()

        ' 保存透明度设置到配置文件
        SaveOpacitySetting(Me.Opacity)
    End Sub

    ' 保存透明度设置到配置文件
    Private Sub SaveOpacitySetting(opacity As Double)
        Try
            ' 获取配置文件路径
            Dim OpacityConfigFilePath As String = Path.Combine(configFilePath, "opacity.json")

            ' 读取现有配置
            Dim settings As New Dictionary(Of String, Object)
            If File.Exists(OpacityConfigFilePath) Then
                Dim json = File.ReadAllText(OpacityConfigFilePath)
                settings = JsonSerializer.Deserialize(Of Dictionary(Of String, Object))(json)
            End If

            ' 保存透明度设置
            settings("opacity") = opacity

            ' 保存回文件
            Dim jsonOutput = JsonSerializer.Serialize(settings, New JsonSerializerOptions With {.WriteIndented = True})
            File.WriteAllText(OpacityConfigFilePath, jsonOutput)

        Catch ex As Exception
            MessageBox.Show("保存透明度设置失败：" & ex.Message)
        End Try
    End Sub

    ' 读取透明度设置
    Private Sub LoadOpacitySetting()
        Try
            ' 获取配置文件路径
            Dim OpacityConfigFilePath As String = Path.Combine(configFilePath, "opacity.json")

            ' 读取现有配置
            If File.Exists(OpacityConfigFilePath) Then
                Dim json = File.ReadAllText(OpacityConfigFilePath)
                Dim settings = JsonSerializer.Deserialize(Of Dictionary(Of String, Object))(json)

                ' 获取透明度设置并应用
                If settings.ContainsKey("opacity") Then
                    ' 提取 JsonElement 并转换为 Double 类型
                    Me.Opacity = settings("opacity").GetDouble()
                End If
            End If
        Catch ex As Exception
            MessageBox.Show("读取透明度设置失败：" & ex.Message)
        End Try
    End Sub


    Private Function GetTaskFromSource(src As Control) As TaskItem
        'If src Is Nothing Then Return Nothing
        'If TypeOf src.Tag Is TaskItem Then Return DirectCast(src.Tag, TaskItem)

        '' Panel 之类嵌套，尝试往上找
        'Dim p = src
        'While p IsNot Nothing
        '    If TypeOf p.Tag Is TaskItem Then Return DirectCast(p.Tag, TaskItem)
        '    p = p.Parent
        'End While
        'Return Nothing
        Return GetTaskFromAny(src)
    End Function

    ' 完成任务 → 灰色 + 删除线；未完成 → 主色 + 去除删除线
    Private Sub ApplyTaskLabelStyle(lbl As Label, task As TaskItem)
        If task.IsCompleted Then
            lbl.ForeColor = Color.DimGray
            Dim newStyle As FontStyle = lbl.Font.Style Or FontStyle.Strikeout
            If lbl.Font.Style <> newStyle Then
                lbl.Font = New Font(lbl.Font, newStyle)
            End If
        Else
            lbl.ForeColor = TextPrimary
            Dim newStyle As FontStyle = lbl.Font.Style And Not FontStyle.Strikeout
            If lbl.Font.Style <> newStyle Then
                lbl.Font = New Font(lbl.Font, newStyle)
            End If
        End If
    End Sub
    '==================== 定时提醒 ====================
    Private Sub TimerCheck_Tick(sender As Object, e As EventArgs) Handles TimerCheck.Tick
        Me.Invoke(Sub()
                      Dim nowT = DateTime.Now
                      'Dim dueList = TaskStore.Tasks.Where(Function(t) (Not t.IsCompleted) AndAlso
                      '                                                (Not t.HasNotified) AndAlso
                      '                                                t.DueAt <= nowT AndAlso
                      '                                                nowT >= t.DueAt.AddMinutes(-30)).
                      '            OrderBy(Function(t) t.DueAt).ToList()

                      Dim dueList = TaskStore.Tasks.
                            Where(Function(t) Not t.IsCompleted AndAlso
                                             Not t.HasNotified AndAlso
                                             nowT >= t.DueAt.AddMinutes(-ReminderLeadMinutes) AndAlso
                                             nowT < t.DueAt).
                            OrderBy(Function(t) t.DueAt).
                            ToList()

                      For Each t In dueList
                          t.HasNotified = True
                          ' 显示托盘气泡通知
                          ShowTrayNotification(t)
                      Next

                      If dueList.Count > 0 Then TaskStore.NotifyChanged()  'TaskStore.Update(dueList(0)) ' 任意触发一次保存+广播

                      ' 检查系统日期是否变化
                      If Date.Today <> FormCalendar.currentMonth Then
                          FormCalendar.currentMonth = Date.Today
                          'FormCalendar.RenderMonth()
                          RenderTasks() ' 更新待办任务
                      End If
                  End Sub)

        Me.Invoke(Sub()
                      ' === 自动迁移：过期未完成 & 未迁移 的待办 → 备忘 ===
                      Dim nowT = DateTime.Now
                      Dim needMigrate = TaskStore.Tasks.Where(Function(t) Not t.IsCompleted AndAlso
                                                    Not t.MigratedToMemo AndAlso
                                                    t.DueAt < nowT).ToList()
                      If needMigrate.Count > 0 Then
                          For Each t In needMigrate
                              Dim tagText = $"{t.Description}（{t.DueAt:MM月dd日未完成任务}）"
                              MemoStore.Add(New MemoItem With {.Text = tagText})
                              t.MigratedToMemo = True
                              TaskStore.Update(t) ' 保存迁移标记
                          Next
                      End If
                  End Sub)
    End Sub

    Private Sub ShowTrayNotification(task As TaskItem)
        Try
            If NotifyIcon1 Is Nothing Then
                InitializeNotifyIcon()
            End If

            ' 确保NotifyIcon可见
            NotifyIcon1.Visible = True

            Dim minsLeft As Integer = Math.Max(0, CInt(Math.Ceiling((task.DueAt - DateTime.Now).TotalMinutes)))

            ' 设置气泡提示内容
            NotifyIcon1.BalloonTipTitle = "📋 待办提醒"
            'NotifyIcon1.BalloonTipText = $"⏰ {task.DueAt:HH:mm} - {task.Description}"
            NotifyIcon1.BalloonTipText = $"还有{minsLeft}分钟：{task.DueAt:HH:mm} - {task.Description}"
            NotifyIcon1.BalloonTipIcon = ToolTipIcon.Info

            ' 显示气泡提示
            NotifyIcon1.ShowBalloonTip(8000) ' 8秒显示时间

            Logger.Log($"显示气泡提醒: {task.Description}, 还有{minsLeft}分钟")
        Catch ex As Exception
            Logger.Log($"显示托盘通知失败: {ex.Message}")
            ' 备用方案：使用消息框
            MessageBox.Show($"{task.TaskDate:yyyy-MM-dd} {task.DueAt:HH:mm}" & Environment.NewLine &
                       task.Description, "待办提醒", MessageBoxButtons.OK, MessageBoxIcon.Information)
        End Try
    End Sub

    Private Sub SaveTasksToDisk()
        Try
            If Not Directory.Exists(dataDir) Then Directory.CreateDirectory(dataDir)
            Dim json = JsonSerializer.Serialize(TaskStore.Tasks, New JsonSerializerOptions With {.WriteIndented = True})
            File.WriteAllText(dataFile, json)
        Catch ex As Exception
            MessageBox.Show("保存待办文件失败：" & ex.Message)
        End Try
    End Sub

    Private Sub OpenCalendar()
        Dim f As New FormCalendar With {
            .OnTasksChanged = Sub()
                                  ' 任务变化后刷新右侧任务窗
                                  RenderTasks()
                              End Sub,
            .StartPosition = FormStartPosition.CenterScreen
        }
        f.Show()  ' 非模式，便于两边联动
    End Sub

    Private Sub IconMenu_ItemClicked(sender As Object, e As ToolStripItemClickedEventArgs) Handles IconMenu.ItemClicked
        Dim cms = DirectCast(sender, ContextMenuStrip)
        Dim src = cms.SourceControl
        Dim task = TryCast(GetTaskFromSource(src), TaskItem)

        Select Case e.ClickedItem.Name
            Case "ShowTaskinfo"
                Me.Show()
                Me.BringToFront()

            Case "CloseTaskinfo"
                Me.Hide()

            Case "ShowCalendar"
                FormCalendar.Show()
                FormCalendar.BringToFront()

            Case "CloseCalendar"
                FormCalendar.Hide()

            Case "SearchFileContant"
                SearchMainForm.Show()

            Case "Config"
                ' 窗口不在任务栏显示
                Dim settingsForm As New FormSettings With {
                    .ShowInTaskbar = False
                }
                settingsForm.ShowDialog()

            Case "AppExit"
                Application.Exit()
        End Select


    End Sub

    ' 从任意控件（含父链）提取 TaskItem（若有）
    Private Function GetTaskFromAny(src As Control) As TaskItem
        'Dim p = src
        'While p IsNot Nothing
        '    If TypeOf p.Tag Is TaskItem Then Return DirectCast(p.Tag, TaskItem)
        '    p = p.Parent
        'End While
        'Return Nothing
        Dim p = src
        While p IsNot Nothing
            If TypeOf p.Tag Is String Then
                Dim id = DirectCast(p.Tag, String)
                Dim t = TaskStore.FindById(id)
                If t IsNot Nothing Then Return t
            ElseIf TypeOf p.Tag Is TaskItem Then
                Return DirectCast(p.Tag, TaskItem) ' 兼容旧代码
            End If
            p = p.Parent
        End While
        Return Nothing
    End Function

    ' 从 SourceControl 推断想要的日期/时间默认值
    Private Sub InferDateTimeFromSource(src As Control, ByRef outDate As Date, ByRef outDue As DateTime)
        Dim ti = GetTaskFromAny(src)
        If ti IsNot Nothing Then
            outDate = ti.TaskDate.Date
            outDue = ti.DueAt
            Exit Sub
        End If

        ' 如果是日期标题 Label（Tag 存的是 Date）
        If TypeOf src Is Label AndAlso TypeOf src.Tag Is Date Then
            Dim d As Date = DirectCast(src.Tag, Date)
            outDate = d.Date
            outDue = d.Date.AddHours(9) ' 日期行新建默认 09:00
            Exit Sub
        End If

        ' 兜底：今天 09:00
        outDate = Date.Today
        outDue = Date.Today.AddHours(9)
    End Sub

    ' 窗口移动事件：鼠标按下
    Private Sub FormMain_MouseDown(sender As Object, e As MouseEventArgs) Handles MyBase.MouseDown
        If e.Button = MouseButtons.Left Then
            isMouseDown = True
            mouseOffset = New Point(e.X, e.Y)
        End If
    End Sub

    ' 窗口移动事件：鼠标拖动
    Private Sub FormMain_MouseMove(sender As Object, e As MouseEventArgs) Handles MyBase.MouseMove
        If isMouseDown Then
            ' 移动窗口
            Me.Location = New Point(Me.Left + e.X - mouseOffset.X, Me.Top + e.Y - mouseOffset.Y)
        End If

        ' 调整大小
        If e.X >= Me.Width - resizeAreaSize AndAlso e.Y >= Me.Height - resizeAreaSize Then
            Me.Cursor = Cursors.SizeNWSE
            If e.Button = MouseButtons.Left Then
                Me.Width = e.X
                Me.Height = e.Y
            End If
        ElseIf e.X >= Me.Width - resizeAreaSize Then
            Me.Cursor = Cursors.SizeWE
        ElseIf e.Y >= Me.Height - resizeAreaSize Then
            Me.Cursor = Cursors.SizeNS
        Else
            Me.Cursor = Cursors.Default
        End If
    End Sub

    ' 窗口移动事件：鼠标松开
    Private Sub FormMain_MouseUp(sender As Object, e As MouseEventArgs) Handles MyBase.MouseUp
        isMouseDown = False
    End Sub
End Class

Public Class TaskItem
    Public Property Id As String = Guid.NewGuid().ToString("N")
    Public Property TaskDate As Date            ' 分组日期（仅日期部分）
    Public Property Description As String
    Public Property IsCompleted As Boolean
    Public Property DueAt As DateTime           ' 实际提醒时间（含日期+时分）
    Public Property HasNotified As Boolean      ' 是否已提醒（避免重复）

    ' —— 新增：是否已迁移到备忘，避免重复转入 ——
    Public Property MigratedToMemo As Boolean
End Class

